/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.tools.idea.gradle.project.sync.runsIndexingWithGradlePhasedSync

import com.android.tools.idea.gradle.project.sync.snapshots.TestProject
import com.android.tools.idea.gradle.project.sync.snapshots.TestProjectDefinition.Companion.prepareTestProject
import com.android.tools.idea.testing.AndroidProjectRule
import com.android.tools.idea.testing.IntegrationTestEnvironmentRule
import com.android.tools.idea.testing.aggregateAndThrowIfAny
import com.android.tools.idea.testing.runCatchingAndRecord
import com.android.tools.idea.testing.verifySyncSkipped
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertWithMessage
import com.intellij.openapi.project.Project
import com.intellij.platform.backend.workspace.workspaceModel
import com.intellij.workspaceModel.ide.impl.WorkspaceModelImpl
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

private fun getProjectSpecificReopenIssues(testProject: TestProject) = when(testProject) {
  TestProject.COMPATIBILITY_TESTS_AS_36,
  TestProject.SIMPLE_APPLICATION_NOT_AT_ROOT,
  TestProject.KOTLIN_MULTIPLATFORM_WITHJS -> setOf("/ModuleFile")
  else -> emptySet()
}

private fun getProjectSpecificIdeModelReopenIssues(testProject: TestProject) = setOf<String>()

fun ModuleDumpWithType.filterOutProjectSpecificReopenIssues(testProject: TestProject) = copy(
  projectStructure = projectStructure.filter { line ->
    getProjectSpecificReopenIssues(testProject).none { line.contains(it) }
  },
  ideModels = ideModels.filter { line ->
    getProjectSpecificIdeModelReopenIssues(testProject).none { line.contains(it) }
  }
)

@RunWith(Parameterized::class)
class PhasedSyncReopenTests(val testProject: TestProject) : PhasedSyncSnapshotTestBase() {
  @get:Rule val projectRule: IntegrationTestEnvironmentRule = AndroidProjectRule.withIntegrationTestEnvironment()

  @Test
  fun testLoadAndCompareFullState() {
    setupPhasedSyncIntermediateStateCollector(projectRule.testRootDisposable)
    aggregateAndThrowIfAny {
      val preparedProject = projectRule.prepareTestProject(testProject)
      val fullSyncEndState = preparedProject.open({ it.copy(expectedSyncIssues = testProject.expectedSyncIssues) }) { project: Project ->
        project.dumpModules(knownAndroidPaths)
      }

      val reopenState = preparedProject.open({ it.copy(skipSwitchingVariants = true) }) { project: Project ->
        assertWithMessage("Workspace model cache failed to load!").that(
          (project.workspaceModel as WorkspaceModelImpl).loadedFromCache
        ).isEqualTo(true)
        verifySyncSkipped(project, projectRule.testRootDisposable)
        project.dumpModules(knownAndroidPaths)
      }

      println("Full sync Project structure: ${fullSyncEndState.projectStructure()}")
      println("Full sync Ide models: ${fullSyncEndState.ideModels()}")
      println("Reopen    Project structure: ${reopenState.projectStructure()}")
      println("Reopen    Ide models: ${reopenState.ideModels()}")

      runCatchingAndRecord {
        Truth.assertWithMessage("Comparing full sync end state for project structure after re-open")
          .that(reopenState.filterOutProjectSpecificReopenIssues(testProject).projectStructure())
          .isEqualTo(fullSyncEndState.filterOutProjectSpecificReopenIssues(testProject).projectStructure())
      }

      runCatchingAndRecord {
        Truth.assertWithMessage("Comparing full sync end state for ide models after re-open")
          .that(reopenState.ideModels())
          .isEqualTo(fullSyncEndState.ideModels())
      }
    }

  }


  companion object {
    @JvmStatic
    @Parameterized.Parameters(name = "{0}")
    fun testParameters(): Collection<*>  = phasedSyncTestProjects.filterNot {
      setOf(
        // TODO(b/384022658): We already fail to re-open these projects due to validation in startup activity failing to find missing jar
        // files that are generated by build. It's not clear whether this is phased sync behaviour or not.
        TestProject.PRIVACY_SANDBOX_SDK,
        TestProject.NON_STANDARD_SOURCE_SET_DEPENDENCIES_MANUAL_TEST_FIXTURES_WORKAROUND,
        // TODO(b/384022658): This one fails to restore the full state and triggers a sync.
        TestProject.SIMPLE_APPLICATION_MULTIPLE_ROOTS,
      ).contains(it)
    }
  }
}
